﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GSdk.Net.Lglcd;
using CoreAudioApi;
using System.Drawing;
using System.Threading;
using GSdk.Shared;

namespace GSdk.Audio
{
    using TimersTimer = System.Timers.Timer;

    class AudioApplet : IDisposable
    {
        /// <summary>
        /// Used to sync the object
        /// </summary>
        public readonly object SyncRoot = new object();

        #region Private and internals
        private volatile int m_Volume;
        private volatile bool m_Muted;

        private const AppletPriorities AlertPriority = AppletPriorities.Alert;
        private const AppletPriorities IdlePriority = AppletPriorities.IdleNoShow;

        private Applet Applet;
        private MMDevice m_AudioDevice;
        private MMDeviceEnumerator m_DevEnum;

        private AudioEndpointVolumeNotificationDelegate m_AudioEndPointVolumeNotificationHandler;
        private EventHandler m_ShutdownAppletHandler;
        private System.Timers.ElapsedEventHandler m_HideHandler;

        private Bitmap m_BwImage;
        private Bitmap m_QvgaImage;

        private Graphics m_BwGraphics;
        private Graphics m_QvgaGraphics;

        private SolidBrush m_VolumeBarColor;
        private Font m_QvgaFont;
        private Font m_BwFont;
        private TimersTimer m_HideTimer;
        #endregion

        public int Volume { get { return m_Volume; } set { m_Volume = value; } }
        public bool Muted { get { return m_Muted; } set { m_Muted = value; } }

        private void ShutdownAppletHandler(object sender, EventArgs e)
        {
            App.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                App.Current.Shutdown();
            }));
        }

        private void AudioEndPointVolumeNotificationHandler(AudioVolumeNotificationData data)
        {
            m_Volume = (int)(data.MasterVolume * 100);
            m_Muted = data.Muted;
            UpdateGraphics();
            GC.Collect();
        }

        private void HideHandler(object sender, System.Timers.ElapsedEventArgs e)
        {
            lock (SyncRoot)
            {
                (Applet.Devices as DefaultDeviceCollection).Qvga.ForegroundApplet = false;
                (Applet.Devices as DefaultDeviceCollection).BlackAndWhite.ForegroundApplet = false;
                (Applet.Devices as DefaultDeviceCollection).Qvga.AppletPriority = IdlePriority;
                (Applet.Devices as DefaultDeviceCollection).BlackAndWhite.AppletPriority = IdlePriority;

                try
                {
                    (Applet.Devices as DefaultDeviceCollection).Qvga.Update(m_QvgaImage);
                }
                catch { }
                try
                {
                    (Applet.Devices as DefaultDeviceCollection).BlackAndWhite.Update(m_BwImage);
                }
                catch { }
                m_HideTimer.Stop();
            }
        }

        private void UpdateGraphics()
        {
            lock (SyncRoot)
            {
                (Applet.Devices as DefaultDeviceCollection).Qvga.ForegroundApplet = true;
                (Applet.Devices as DefaultDeviceCollection).BlackAndWhite.ForegroundApplet = true;
                (Applet.Devices as DefaultDeviceCollection).Qvga.AppletPriority = AlertPriority;
                (Applet.Devices as DefaultDeviceCollection).BlackAndWhite.AppletPriority = AlertPriority;

                UpdateBitmaps();

                try
                {
                    (Applet.Devices as DefaultDeviceCollection).Qvga.Update(m_QvgaImage);
                }
                catch { }
                try
                {
                    (Applet.Devices as DefaultDeviceCollection).BlackAndWhite.Update(m_BwImage);
                }
                catch { }

                // Reset timer, if running
                if (m_HideTimer.Enabled)
                    m_HideTimer.Stop();
                m_HideTimer.Enabled = true;
                m_HideTimer.Start();
            }
        }

        private void UpdateBitmaps()
        {
            UpdateQvgaBitmap();
            UpdateBwBitmap();
        }

        private void UpdateBwBitmap()
        {
            Graphics g;

            g = m_BwGraphics;
                GraphicHelpers.CreateBasicBwInterface(g, m_BwFont, Applet.Name);
                // Write empty bar
                g.DrawImage(GSdk.Audio.Properties.Resources.BWAudio_emptybar, 
                    10, 24, 
                    GSdk.Audio.Properties.Resources.BWAudio_emptybar.Width, GSdk.Audio.Properties.Resources.BWAudio_emptybar.Height
                );

                // Write mute icon
                if (m_Muted)
                    g.DrawImage(GSdk.Audio.Properties.Resources.BWAudio_muted,
                        71, 10,
                        GSdk.Audio.Properties.Resources.BWAudio_muted.Width, GSdk.Audio.Properties.Resources.BWAudio_muted.Height
                    );
                else
                    g.DrawImage(GSdk.Audio.Properties.Resources.BWAudio_notmuted,
                        73, 11,
                        GSdk.Audio.Properties.Resources.BWAudio_notmuted.Width, GSdk.Audio.Properties.Resources.BWAudio_notmuted.Height
                    );

                // Fill volume bar (width = 136, height = 8)
                // 136 : 100 = x : m_Volume
                g.FillRectangle(Brushes.Black, 12, 26, (136 * m_Volume) / 100, 8);
            g.Flush();
        }

        private const int maxSmallBarsAmount = 38;
        private void UpdateQvgaBitmap()
        {
            Graphics g;
            // maxSmallBarsAmount : 100 = x : currentVolume
            int smallBarsAmount = (m_Volume * maxSmallBarsAmount) / 100;

            g = m_QvgaGraphics;
                // Build header
                GraphicHelpers.CreateBasicQvgaInterface(g, m_QvgaFont, GSdk.Audio.Properties.Resources.LCDMedia_Background, GSdk.Audio.Properties.Resources.LCDMedia_headericon, Applet.Name);

                // Build volume bar
                g.DrawImage(GSdk.Audio.Properties.Resources.LCDMedia_barempty_PNG, 9, 217, GSdk.Audio.Properties.Resources.LCDMedia_barempty_PNG.Width, GSdk.Audio.Properties.Resources.LCDMedia_barempty_PNG.Height);

                if (smallBarsAmount > 0)
                {
                    g.DrawImage(GSdk.Audio.Properties.Resources.LCDMedia_bar_left, 8, 217, GSdk.Audio.Properties.Resources.LCDMedia_bar_left.Width, GSdk.Audio.Properties.Resources.LCDMedia_bar_left.Height);
                    // colore argb: 255 0 153 102
                    if (smallBarsAmount > 1)
                        g.FillRectangle(m_VolumeBarColor,
                            8 + GSdk.Audio.Properties.Resources.LCDMedia_bar_left.Width,
                            217 + 1,
                            GSdk.Audio.Properties.Resources.LCDMedia_bar_left.Width * (smallBarsAmount - (smallBarsAmount < maxSmallBarsAmount ? 1 : 2)),
                            GSdk.Audio.Properties.Resources.LCDMedia_bar_left.Height - 2);
                }
                if (smallBarsAmount == maxSmallBarsAmount)
                {
                    g.DrawImage(GSdk.Audio.Properties.Resources.LCDMedia_bar_right, 304, 217, GSdk.Audio.Properties.Resources.LCDMedia_bar_right.Width, GSdk.Audio.Properties.Resources.LCDMedia_bar_right.Height);
                }
                // Gradient
                if (smallBarsAmount > 0)
                    g.DrawImage(GSdk.Audio.Properties.Resources.LCDMedia_gradient_volume,
                        new Rectangle(
                            9, 217,
                            (smallBarsAmount * GSdk.Audio.Properties.Resources.LCDMedia_bar_left.Width) - 1,
                            GSdk.Audio.Properties.Resources.LCDMedia_gradient_volume.Height
                        ),
                        new Rectangle(
                            0, 0,
                            (smallBarsAmount * GSdk.Audio.Properties.Resources.LCDMedia_bar_left.Width) - 1,
                            GSdk.Audio.Properties.Resources.LCDMedia_gradient_volume.Height
                        ),
                        GraphicsUnit.Pixel
                    );
                // Mute image
                if (!m_Muted)
                    g.DrawImage(GSdk.Audio.Properties.Resources.LCDMedia_nomute,
                        148, 166,
                        GSdk.Audio.Properties.Resources.LCDMedia_nomute.Width, GSdk.Audio.Properties.Resources.LCDMedia_nomute.Height
                    );
                else
                    g.DrawImage(GSdk.Audio.Properties.Resources.LCDMedia_mute,
                        148, 170,
                        GSdk.Audio.Properties.Resources.LCDMedia_mute.Width, GSdk.Audio.Properties.Resources.LCDMedia_mute.Height
                    );
            g.Flush();
        }

        public AudioApplet()
        {
            // Allocating resources
            m_AudioEndPointVolumeNotificationHandler = new AudioEndpointVolumeNotificationDelegate(AudioEndPointVolumeNotificationHandler);
            m_ShutdownAppletHandler = new EventHandler(ShutdownAppletHandler);
            m_HideHandler = new System.Timers.ElapsedEventHandler(HideHandler);
            m_DevEnum = new MMDeviceEnumerator();
            m_QvgaFont = new Font(
                "monospace",
                12.0f,
                FontStyle.Bold
            );
            m_BwFont = new Font(
                "monospace",
                6.0f,
                FontStyle.Bold
            );
            m_VolumeBarColor = new SolidBrush(Color.FromArgb(255, 0, 153, 102));
            m_HideTimer = new TimersTimer();
            m_AudioDevice = m_DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);

            // Timer settings
            m_HideTimer.Interval = 3000.0;
            m_HideTimer.AutoReset = false;
            m_HideTimer.Elapsed += m_HideHandler;

            // Creating applet
            Applet = new Applet()
            {
                Name = "GSdk.Audio",
                Autostartable = true,
                SupportedDevices = DeviceTypes.BlackAndWhite | DeviceTypes.Qvga,
            };
            try
            {
                Applet.Connect();
            }
            catch
            {
                App.Current.Shutdown();
                return;
            }
            Applet.ConnectionLost += m_ShutdownAppletHandler;

            (Applet.Devices as DefaultDeviceCollection).Qvga.AppletPriority = IdlePriority;
            (Applet.Devices as DefaultDeviceCollection).BlackAndWhite.AppletPriority = IdlePriority;

            m_AudioDevice.AudioEndpointVolume.OnVolumeNotification += m_AudioEndPointVolumeNotificationHandler;

            // Generating basic graphics
            m_BwImage = new Bitmap(160, 43, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            m_QvgaImage = new Bitmap(320, 240, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            m_BwGraphics = Graphics.FromImage(m_BwImage);
            m_QvgaGraphics = Graphics.FromImage(m_QvgaImage);

            // Storing basic values
            m_Volume = (int)(m_AudioDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100);
            m_Muted = m_AudioDevice.AudioEndpointVolume.Mute;

            // Run first update to show app on the device
            UpdateGraphics();
        }

        #region Dispose handling
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Managed resources
                m_AudioDevice.AudioEndpointVolume.OnVolumeNotification -= m_AudioEndPointVolumeNotificationHandler;
                Applet.Disconnect();
            }

            // Unmanaged resources
            try
            {
                if (m_HideTimer.Enabled)
                    m_HideTimer.Stop();
                m_HideTimer.Dispose();
                m_BwFont.Dispose();
                m_QvgaFont.Dispose();
                m_QvgaGraphics.Dispose();
                m_BwGraphics.Dispose();
                m_AudioDevice.AudioEndpointVolume.Dispose();
                Applet.Dispose();
                m_BwImage.Dispose();
                m_QvgaImage.Dispose();
            }
            catch { }
        }

        ~AudioApplet()
        {
            Dispose(false);
        }
        #endregion
    }
}
